home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 3
/
Cream of the Crop 3.iso
/
comm
/
wnos5src.zip
/
KERNEL.C
< prev
next >
Wrap
Text File
|
1993-11-17
|
13KB
|
538 lines
#undef PROCLOG
/* Non pre-empting synchronization kernel, machine-independent portion */
#if defined(PROCLOG) || defined(PROCTRACE)
#include <stdio.h>
#endif
#include <dos.h>
#include <setjmp.h>
#include "global.h"
#include "mbuf.h"
#include "proc.h"
#include "timer.h"
#include "socket.h"
#include "daemon.h"
#include "hardware.h"
#ifdef PROCLOG
#include "files.h"
#endif
#ifdef PROCLOG
FILE *proclog;
#endif
struct proc *Curproc = NULLPROC; /* Currently running process */
struct proc *Rdytab = NULLPROC; /* Processes ready to run (not including curproc) */
struct proc *Waittab = NULLPROC; /* Waiting process list */
struct proc *Susptab = NULLPROC; /* Suspended processes */
static struct mbuf *Killq;
/*----------------------------------------------------------------------*
* primitive semaphore operation to serialize resource access (DK5DC) *
* semawait waits for a semaphore to become free (0). *
* If req is True, the semaphore will be incremented immediatly *
* *
* semrel will decrement the semaphore and set it to 0 if it becomes *
* negative *
*-----------------------------------------------------------------------*/
void
semwait(int *sema,int req)
{
while(*sema > 0) {
pwait(sema);
}
if(req) {
*sema += 1; /* now lock it */
}
}
void
semrel(int *sema)
{
if((*sema -= 1) <= 0) {
*sema = 0;
}
psignal(sema,0); /* signal to everbody who's waiting*/
}
/* Append proc entry to end of appropriate list */
static void near
addproc(struct proc *entry) /* Pointer to entry */
{
int i_state;
struct proc **head;
if(entry == NULLPROC) {
return;
}
switch(entry->state){
case READY:
head = &Rdytab;
break;
case WAITING:
head = &Waittab;
break;
case SUSPEND:
case SUSPEND | WAITING:
head = &Susptab;
break;
}
i_state = dirps();
entry->next = NULLPROC;
if(*head == NULLPROC) {
/* Empty list, stick at beginning */
*head = entry;
} else {
struct proc *pp = *head;
/* Find last entry on list */
for( ; pp->next != NULLPROC; pp = pp->next) ;
pp->next = entry;
}
restore(i_state);
}
/* Remove a process entry from the appropriate table */
static void near
delproc(struct proc *entry) /* Pointer to entry */
{
int i_state;
struct proc *pp = NULLPROC, *pplast = NULLPROC, **head;
if(entry == NULLPROC) {
return;
}
i_state = dirps();
switch(entry->state) {
case READY:
head = &Rdytab;
break;
case WAITING:
head = &Waittab;
break;
case SUSPEND:
case SUSPEND|WAITING:
head = &Susptab;
break;
}
for(pp = *head; pp != NULLPROC; pplast = pp, pp = pp->next) {
if(pp == entry) {
if(pplast != NULLPROC) {
pplast->next = pp->next;
} else {
*head = pp->next;
}
break;
}
}
restore(i_state);
}
/* Create a process descriptor for the main function. Must be actually
* called from the main function!
* Note that standard I/O is NOT set up here.
*/
struct proc *
mainproc(char *name)
{
/* Create process descriptor */
/* Don't call the xallocw functions! - DB3FL.920801 */
struct proc *pp = mxalloc(sizeof(struct proc));
/* Create name */
sprintf(pp->name,"%.16s",name);
pp->stksize = 0;
/* Make current */
pp->state = READY;
Curproc = pp;
#ifdef PROCLOG
proclog = fopen("/proclog",APPEND_TEXT);
#endif
return pp;
}
/* Create a new, ready process and return pointer to descriptor.
* The general registers are not initialized, but optional args are pushed
* on the stack so they can be seen by a C function.
*/
struct proc *
newproc(
char *name, /* Arbitrary user-assigned name string */
unsigned int stksize, /* Stack size in words to allocate */
void (*pc)(), /* Initial execution address */
int iarg, /* Integer argument (argc) */
void *parg1, /* Generic pointer argument #1 (argv) */
void *parg2, /* Generic pointer argument #2 (session ptr) */
int freeargs) /* if set, free args list on termination */
{
struct proc *pp;
int i;
#ifdef MDEBUG
chkstk();
#endif
/* Create process descriptor */
pp = mxallocw(sizeof(struct proc));
/* Allocate stack */
pp->stack = cxallocw(sizeof(int16),stksize);
pp->stksize = stksize;
/* Create name */
sprintf(pp->name,"%.16s",name);
/* Initialize stack for high-water check */
for(i = 0; i < pp->stksize; i++) {
pp->stack[i] = STACKPAT;
}
/* Do machine-dependent initialization of stack */
psetup(pp,iarg,parg1,parg2,pc);
pp->freeargs = freeargs;
pp->iarg = iarg;
pp->parg1 = parg1;
pp->parg2 = parg2;
/* Inherit creator's input and output sockets */
usesock(Curproc->input);
pp->input = Curproc->input;
usesock(Curproc->output);
pp->output = Curproc->output;
/* Add to ready process table */
pp->state = READY;
addproc(pp);
return pp;
}
/* Free resources allocated to specified process. If a process wants to kill
* itself, the reaper is called to do the dirty work. This avoids some
* messy situations that would otherwise occur, like freeing your own stack.
*/
void
killproc(struct proc *pp)
{
#ifdef PROCLOG
extern int stkutil __ARGS((struct proc *pp));
#endif
if(pp == NULLPROC) {
return;
}
/* Don't check the stack here!
* Will cause infinite recursion if called from a stack error.
*/
if(pp == Curproc) {
killself(); /* Doesn't return */
}
/* Close any open sockets */
freesock(pp);
close_s(pp->input);
close_s(pp->output);
/* Stop alarm clock in case it's running */
stop_timer(&pp->alarm);
/* Alert everyone waiting for this proc to die */
psignal(pp,0);
/* Remove from appropriate table */
delproc(pp);
#ifdef PROCLOG
if(!uploadstatus) {
fprintf(proclog,"size %5u max %5u name %s\n",
pp->stksize,stkutil(pp),pp->name);
fflush(proclog);
}
#endif
/* Free allocated memory resources */
if(pp->freeargs){
char **argv = pp->parg1;
while(pp->iarg-- != 0) {
xfree(*argv++);
}
xfree(pp->parg1);
}
xfree(pp->stack);
xfree(pp);
}
/* Terminate current process by sending a request to the killer process.
* Automatically called when a process function returns. Does not return.
*/
void
killself(void)
{
if(Curproc != NULLPROC) {
struct mbuf *bp = pushdown(NULLBUF,sizeof(Curproc));
memcpy(bp->data,(char *)&Curproc,sizeof(Curproc));
enqueue(&Killq,bp);
}
/* "Wait for me; I will be merciful and quick." */
for(;;) {
pwait(NULL);
}
}
/* Process used by processes that want to kill themselves */
void
killer(int i,void *v1,void *v2)
{
for(;;) {
struct proc *pp;
struct mbuf *bp;
while(Killq == NULLBUF) {
pwait(&Killq);
}
bp = dequeue(&Killq);
pullup(&bp,(char *)&pp,sizeof(pp));
free_p(bp);
if(pp != Curproc) { /* We're immortal */
killproc(pp);
}
}
}
/* Inhibit a process from running */
void
suspend(struct proc *pp)
{
if(pp == NULLPROC) {
return;
}
if(pp != Curproc) {
delproc(pp); /* Running process isn't on any list */
}
pp->state |= SUSPEND;
if(pp != Curproc) {
addproc(pp); /* pwait will do it for us */
} else {
pwait(NULL);
}
}
/* Restart suspended process */
void
resume(struct proc *pp)
{
if(pp == NULLPROC) {
return;
}
delproc(pp); /* Can't be Curproc! */
pp->state &= ~SUSPEND;
addproc(pp);
}
/* Wakeup waiting process, regardless of event it's waiting for. The process
* will see a return value of "val" from its pwait() call.
*/
void
alert(struct proc *pp,void *val)
{
if(pp == NULLPROC) {
return;
}
#ifdef XXX
if((pp->state & WAITING) == 0)
return;
#endif
#ifdef PROCTRACE
tprintf("alert(%lx,%u) [%s]\n",ptol(pp),val,pp->name);
#endif
if(pp != Curproc) {
delproc(pp);
}
pp->state &= ~WAITING;
pp->retval = val;
pp->event = 0;
if(pp != Curproc) {
addproc(pp);
}
}
/* Post a wait on a specified event and give up the CPU until it happens. The
* null event is special: it means "I don't want to block on an event, but let
* somebody else run for a while". It can also mean that the present process
* is terminating; in this case the wait never returns.
*
* Pwait() returns 0 if the event was signaled; otherwise it returns the
* arg in an alert() call. Pwait must not be called from interrupt level.
*
* Note that pwait can run with interrupts enabled even though it examines
* a few global variables that can be modified by psignal at interrupt time.
* These *seem* safe.
*/
void *
pwait(void *event)
{
struct proc *oldproc;
void *tmp;
if(Curproc != NULLPROC) { /* If process isn't terminating */
#ifdef MDEBUG
chkstk();
#endif
if(event == NULL) {
/* Special case; just give up the processor.
*
* Optimization: if nothing else is ready, just return.
*/
if(Rdytab == NULLPROC) {
return 0;
}
} else {
/* Post a wait for the specified event */
Curproc->event = event;
Curproc->state = WAITING;
}
addproc(Curproc);
}
/* Look for a ready process and run it. If there are none,
* loop or halt until an interrupt makes something ready.
*/
while(Rdytab == NULLPROC) {
/* Give system back to upper-level multitasker, if any.
* Note that this function enables interrupts internally
* to prevent deadlock, but it restores our state
* before returning.
*/
giveup();
}
/* Remove first entry from ready list */
oldproc = Curproc;
Curproc = Rdytab;
delproc(Curproc);
/* Now do the context switch.
* This technique was inspired by Rob, PE1CHL, and is a bit tricky.
*
* If the old process has gone away, simply load the new process's
* environment. Otherwise, save the current process's state. Then if
* this is still the old process, load the new environment. Since the
* new task will "think" it's returning from the setjmp() with a return
* value of 1, the comparison with 0 will bypass the longjmp(), which
* would otherwise cause an infinite loop.
*/
#ifdef PROCTRACE
if(strcmp(oldproc->name,Curproc->name) != 0) {
tprintf("-> %s(%d)\n",Curproc->name,!!Curproc->i_state);
}
#endif
/* Note use of comma operator to save old interrupt state only if
* oldproc is non-null
*/
if(oldproc == NULLPROC
|| (oldproc->i_state = istate(), setjmp(oldproc->env) == 0)) {
/* We're still running in the old task; load new task context.
* The interrupt state is restored here in case longjmp
* doesn't do it (e.g., systems other than Turbo-C).
*/
restore(Curproc->i_state);
longjmp(Curproc->env,1);
}
/* At this point, we're running in the newly dispatched task */
tmp = Curproc->retval;
Curproc->retval = 0;
/* Also restore the true interrupt state here, in case the longjmp
* DOES restore the interrupt state saved at the time of the setjmp().
* This is the case with Turbo-C's setjmp/longjmp.
*/
restore(Curproc->i_state);
return tmp;
}
/* Make ready the first 'n' processes waiting for a given event. The ready
* processes will see a return value of 0 from pwait(). Note that they don't
* actually get control until we explicitly give up the CPU ourselves through
* a pwait(). Psignal may be called from interrupt level. It returns the
* number of processes that were woken up.
*/
int
psignal(
void *event, /* Event to signal */
int n) /* Max number of processes to wake up */
{
struct proc *pp;
int i_state;
int cnt = 0; /* 0 means "signal everybody waiting" */
#ifdef MDEBUG
chkstk();
#endif
if(event == NULL) {
return 0; /* Null events are invalid */
}
if(n == 0) {
n = 32766;
}
i_state = dirps();
for(pp = Waittab; pp != NULLPROC; pp = pp->next) {
if(n == 0) {
break;
}
if(pp->event == event) {
#ifdef PROCTRACE
if(i_state) {
tprintf("psignal(%lx,%u) wake %lx [%s]\n",
ptol(event),n,ptol(pp),pp->name);
}
#endif
delproc(pp);
pp->state &= ~WAITING;
pp->event = 0;
pp->retval = 0;
addproc(pp);
n--;
cnt++;
}
}
for(pp = Susptab; pp != NULLPROC; pp = pp->next) {
if(n == 0) {
break;
}
if(pp->event == event) {
#ifdef PROCTRACE
if(i_state) {
tprintf("psignal(%lx,%u) wake %lx [%s]\n",
ptol(event),n,ptol(pp),pp->name);
}
#endif
delproc(pp);
pp->state &= ~WAITING;
pp->event = 0;
pp->retval = 0;
addproc(pp);
n--;
cnt++;
}
}
restore(i_state);
return cnt;
}